/////////////////////////////////////////////////////
//	File Name	:	"CGame.cpp"
//
//	Author Name	:	Mason Miller
//
//	Purpose	:	To contain all game related code.
/////////////////////////////////////////////////////

#include "CGame.h"
#include <ctime>
#include "CMainMenuState.h"
//#include "Game Objects\CProjectile.h"
//#include "Game Objects\CEnemy.h"
//#include "Game Objects\CPlayer.h"
#include "Game Objects\CObjectManager.h"

CGame::CGame(void)
{
	m_pD3D				=	NULL;
	m_pTM				=	NULL;
	m_pFM				=	NULL;
	m_pDI				=	NULL;
	m_pOM				=	NULL;
	m_pOF				=	NULL;

	m_nScreenWidth		=	SCREEN_WIDTH;
	m_nScreenHeight		=	SCREEN_HEIGHT;

	m_fElapsedTime		=	0;
	m_stopWatch.Reset();
}

CGame::~CGame(void)
{

}

CGame* CGame::GetInstance(void)
{
	//	Lazy instantiation
	static CGame instance;
	return &instance;
}

void CGame::Initialize(HWND hWnd, HINSTANCE hInstance, int nScreenWidth, int nScreenHeight, bool bIsWindowed)
{
	srand(unsigned int(time(0)));

	//	Get pointers to singletons:
	m_pD3D	= CSGD_Direct3D::GetInstance();
	m_pTM	= CSGD_TextureManager::GetInstance();
	m_pFM	= CSGD_FModManager::GetInstance();
	m_pDI	= CSGD_DirectInput::GetInstance();
	m_pOF	= ObjectFactory<string, CBase>::GetInstance();
	m_pOM	= ObjectManager::GetInstance();

#ifdef _DEBUG
	m_bIsWindowed = true;					
#else
	m_bIsWindowed = false;
#endif
	

	//	Initialize singletons:
	m_pD3D->InitDirect3D(hWnd, nScreenWidth, nScreenHeight, bIsWindowed, false); //	Enable/Disable Vsync
	m_pTM->InitTextureManager(m_pD3D->GetDirect3DDevice(), m_pD3D->GetSprite());
	m_pFM->InitFModManager(hWnd);
	m_pDI->InitDirectInput(hWnd, hInstance, DI_KEYBOARD | DI_MOUSE | DI_JOYSTICKS, DI_MOUSE );

	//	Register for events:

	//	Register game objects with the OF

	//	Load common assets:

	//	Create game objects:

	//	Add to the OM
	
	m_nScreenWidth	= nScreenWidth;
	m_nScreenHeight = nScreenHeight;

	m_fMusicVolume = 1.0f;
	m_fSFXVolume = 1.0f;

	// Initialize Sound settings to the proper values

	// Go into the main menu of the game
	PushState(CMainMenuState::GetInstance());

	m_stopWatch.Start();
}

bool CGame::GameMain(void)
{
	/////////////////////////////////////////////////////
	//	Calculate Elapsed Time
	m_fElapsedTime = (float)m_stopWatch.GetElapsedTime();
	m_stopWatch.Reset();

#ifdef _DEBUG
	//	Cap elapsed time
	if(m_fElapsedTime > 0.125f)
		m_fElapsedTime = 0.125f;
#endif

	/////////////////////////////////////////////////////

	//	3 steps a game does every frame:

	//	Input
	if(!Input())
		return false;

	//	Update
	Update();

	//	Draw
	Render();

	//	Count FPS
	//m_nFrameCount++;

	//	Check if 1 second has passed
	//if(GetTickCount() - m_dwFPSTimeStamp > 1000)
	//{
		//	Remember the frame count
		//m_nFPS = m_nFrameCount;
		//	Reset the frame count
		//m_nFrameCount = 0;
		//	Get a new time stamp
		//m_dwFPSTimeStamp = GetTickCount();
	//}

	return true;
}

bool CGame::Input(void)
{
	m_pDI->ReadDevices();	//	typically called ONCE a frame
	
	//	Quit if the user presses ESC
	//if(m_pDI->KeyPressed(DIK_ESCAPE))
		//return false;

	if((m_pDI->KeyDown(DIK_LALT) || m_pDI->KeyDown(DIK_RALT)) && m_pDI->KeyPressed(DIK_RETURN))
	{
			m_bIsWindowed = !m_bIsWindowed;
			m_pD3D->ChangeDisplayParam(640, 480, m_bIsWindowed);
		//if(!m_pGameState->Input())
			//return false;
	}
	//else if(!m_pGameState->Input())
		//return false;

	//	Demonstrate the difference betweem buffered and immediate input audibly.

	//	buffered
	//if(m_pDI->KeyPressed(DIK_1))
		//m_bIsWindowed = false;

	//	immediate
	//if(m_pDI->KeyReleased(DIK_SPACE))
		//m_pMS->SendMsg(new CCreateProjectileMessage(320.0f, 240.0f));

	//if(!m_pGameState->Input())
		//return false;

	return m_vGS.back()->Input();
}

void CGame::Update()
{
	m_fGameTime += m_fElapsedTime;

	//m_fTimeDelay += m_fElapsedTime;

	//if(m_fTimeDelay >= 0.1f)
		//m_fTimeDelay = 0.0f;

	m_pFM->Update();
	m_vGS.back()->Update(m_fElapsedTime);

	//m_pOM->UpdateObjects(m_fElapsedTime);

	//m_pES->ProcessEvents();
	//m_pMS->ProcessMessages();
}

void CGame::Render(void)
{
	m_pD3D->Clear(0, 0, 0);
	m_pD3D->DeviceBegin();
	m_pD3D->SpriteBegin();

	//static float fRot = 0.0f;
	//fRot += 0.01f;

	//	Draw here
	
	//m_pTM->Draw(m_nImageID, 100, 100, 4.0f, 4.0f, NULL, 20.0f, 18.0f, fRot, D3DCOLOR_ARGB(128, 0, 255, 0));
	//m_pD3D->GetSprite()->Flush();	//	tell everything to render now!

	//m_pD3D->DrawTextA("Hello World!", 100, 100);

	// Render states
	for(unsigned int i=0; i<m_vGS.size(); ++i)
	{
		m_vGS[i]->Render();
	}

	//m_pOM->RenderObjects();

	char buffer[128] = {0};
	//sprintf_s(buffer, _countof(buffer), "FPS: %i", m_nFPS);
	//m_pD3D->DrawTextA(buffer, 0, 0);

	m_pD3D->SpriteEnd();
	m_pD3D->DeviceEnd();
	m_pD3D->Present();
}

void CGame::Shutdown(void)
{
	//	Should be the first thing!
	ChangeState(NULL);

	//	Shutdown in the opposite order

	if(m_pOF)
	{
		m_pOF->ShutdownObjectFactory();
		m_pOF = NULL;
	}

	if(m_pOM)
	{
		m_pOM->RemoveAllObjects();
		m_pOM->DeleteInstance();
	}

	//SAFE_RELEASE(m_pProjectile);

	if(m_pDI)
	{
		m_pDI->ShutdownDirectInput();
		m_pDI = NULL;
	}

	if(m_pFM)
	{
		m_pFM->ShutdownFModManager();
		m_pFM = NULL;
	}

	if(m_pTM)
	{
		m_pTM->ShutdownTextureManager();
		m_pTM = NULL;
	}

	if(m_pD3D)
	{
		m_pD3D->ShutdownDirect3D();
		m_pD3D = NULL;
	}

	if(m_pMS)
	{
		m_pMS->ShutdownMessageSystem();
		m_pMS = NULL;
	}

	if(m_pES)
	{
		m_pES->ShutdownEventSystem();
		m_pES = NULL;
	}
}

void CGame::ChangeState(IGameState* pGameState, bool overide)
{
	if(!overide)
	{
		ClearAllStates();
		PushState(pGameState);
	}
	else
	{
		for(unsigned int i = 0; i < m_vGS.size(); i++)
		{
			if(pGameState)
			{
				if(m_vGS[i] != pGameState)
				{
					m_vGS[i]->Exit();
					m_vGS.erase(m_vGS.begin() + i);
					i--;
				}
				else
				{
					m_vGS.erase(m_vGS.begin() + i);
					i--;
				}
			}

		}
		m_vGS.push_back(pGameState);
	}
}

void CGame::PushState(IGameState* state)
{
	if(state)
	{
		m_vGS.push_back(state);
		m_vGS.back()->Enter();
	}
}

void CGame::PopState()
{
	if(!m_vGS.empty())
	{
		m_vGS.back()->Exit();
		m_vGS.pop_back();

	}
}

void CGame::ClearAllStates()
{
	for(unsigned int i=0; i<m_vGS.size(); ++i)
	{
		m_vGS[i]->Exit();
	}
	m_vGS.clear();
}